package bll

import (
	"context"
	"fmt"
	"gitlab.local/backend/proto/dto"
	"go.uber.org/zap"
	log "signal/collector/logger"
	"signal/errors"
	"signal/event"
	"signal/lib/trace_log"
	"signal/model"
	"signal/utils"
)

var Command = &command{}

// 执行自定义命令
type command struct{}

func (c *command) init() func() {
	return func() {}
}

func (c *command) Command(ctx context.Context, in *dto.CommandRequest) error {
	var err error
	switch in.Cmd {
	case dto.CmdType_CmdPowerOn:
		err = c.power(in.Sn, model.DevicePowerOn)
	case dto.CmdType_CmdPowerOff:
		err = c.power(in.Sn, model.DevicePowerOff)
	case dto.CmdType_CmdLock:
		err = c.locker(in.Sn, model.DeviceLock)
	case dto.CmdType_CmdUnlock:
		err = c.locker(in.Sn, model.DeviceUnlock)
	case dto.CmdType_CmdSeatEjection:
		err = c.seatEject(in.Sn)
	case dto.CmdType_CmdHelmetUnlock:
		err = c.helmetUnlock(in.Sn)
	}

	fmt.Println("----------command sn:", in.Sn, "Command: ", in.Cmd, " err: ", err)
	trace_log.DebugLogger(in.Sn, "Command 执行 data:", utils.ToJsonString(in), " err:", err)
	return err
}

func (c *command) Reactive(ctx context.Context, in *dto.ReactiveRequest) error {
	return Session.Send(in.Sn, in.Hex)
}

func (c *command) power(sn string, operate int) error {
	var (
		err        error
		id         uint8
		requestKey string
		res        interface{}
	)

	if requestKey, id, err = Session.GetRequestID(sn, model.CmdPower); err != nil {
		log.Errorf("[%s] get request id error: %+v ", sn, err)
		return err
	}

	pkt := model.BuildPacket(model.CmdPower, id, []byte{uint8(operate)})

	if res, err = Session.SyncRequest(sn, requestKey, pkt); err != nil {
		log.Errorf("send signal to device error:  %s", err)
		_ = Session.ReleaseID(sn, id)
		return err
	}
	log.Infof("send power command to device: %X", pkt.ToHexBytes())

	resp := res.([]byte)
	if resp[0] == 0 {
		log.Errorf("power command execute failed: %v", zap.Int("operate", operate))
		return errors.New("设备返回执行失败")
	}

	eData := &dto.DeviceStatus{
		Sn: sn,
	}
	switch operate {
	case model.DevicePowerOn:
		eData.Event = dto.EventType_PowerOn
	case model.DevicePowerOff:
		eData.Event = dto.EventType_PowerOff
	}
	event.E.PublishAsync(event.DeviceStatusChange, eData)
	trace_log.DebugLogger(sn, "Command power 执行 ", operate, "成功")
	return nil
}

func (c *command) locker(sn string, operate int) error {
	var (
		err        error
		res        interface{}
		requestKey string
		id         uint8
	)

	if requestKey, id, err = Session.GetRequestID(sn, model.CmdLock); err != nil {
		log.Error("get request error ", err)
		return err
	}

	pkt := model.BuildPacket(model.CmdLock, id, []byte{uint8(operate)})

	if res, err = Session.SyncRequest(sn, requestKey, pkt); err != nil {
		log.Errorf("send signal to device error:  %s", err)
		_ = Session.ReleaseID(sn, id)
		return err
	}
	log.Infof("send lock command to device: %X", pkt.ToHexBytes())

	resp := res.([]byte)
	if resp[0] == 0 {
		log.Errorf("lock command execute failed: %v", zap.Int("operate", operate))
		return errors.New("设备返回执行失败")
	}

	eData := &dto.DeviceStatus{
		Sn: sn,
	}

	switch operate {
	case model.DeviceLock:
		eData.Event = dto.EventType_Lock
	case model.DeviceUnlock:
		eData.Event = dto.EventType_Unlock
	}
	event.E.PublishAsync(event.DeviceStatusChange, eData)
	return nil
}

func (c *command) seatEject(sn string) error {
	var (
		err        error
		requestKey string
		res        interface{}
		id         uint8
	)

	if requestKey, id, err = Session.GetRequestID(sn, model.CmdSwitch); err != nil {
		log.Error("get request error ", err)
		return err
	}

	packet := model.BuildPacket(model.CmdSwitch, id, c.buildSeatEjectionHex())
	if res, err = Session.SyncRequest(sn, requestKey, packet); err != nil {
		_ = Session.ReleaseID(sn, id)
		return err
	}
	log.Infof("send lock command to device: %X", packet.ToHexBytes())

	resp := res.([]byte)
	if resp[0] == 0 {
		log.Error("seatEject command execute failed")
		return errors.New("设备返回执行失败")
	}

	return nil
}

func (c *command) helmetUnlock(sn string) error {
	var (
		err        error
		requestKey string
		res        interface{}
		id         uint8
	)

	if requestKey, id, err = Session.GetRequestID(sn, model.CmdSwitch); err != nil {
		log.Error("get request error ", err)
		return err
	}

	packet := model.BuildPacket(model.CmdSwitch, id, c.buildHelmetUnlockHex())
	log.Infof("send helmet unlock command to device: %X", packet.ToHexBytes())

	if res, err = Session.SyncRequest(sn, requestKey, packet); err != nil {
		_ = Session.ReleaseID(sn, id)
		return err
	}
	resp := res.([]byte)
	if resp[0] == 0 {
		log.Error("helmetUnlock command execute failed")
		return errors.New("设备返回执行失败")
	}

	return nil
}

func (c *command) buildSeatEjectionHex() []byte {
	enable := make([]byte, 1)
	val := make([]byte, 1)
	enableWriter := utils.NewBitWriter(enable)
	valWriter := utils.NewBitWriter(val)

	enableWriter.WriteBits8(4, 1)
	valWriter.WriteBits8(4, 1)

	res := make([]byte, 0)
	res = append(res, enable[0])
	res = append(res, val[0])

	return res
}

func (c *command) buildHelmetUnlockHex() []byte {
	enable := make([]byte, 1)
	val := make([]byte, 1)
	enableWriter := utils.NewBitWriter(enable)
	valWriter := utils.NewBitWriter(val)

	enableWriter.WriteBits8(5, 1)
	valWriter.WriteBits8(5, 1)

	res := make([]byte, 0)
	res = append(res, enable[0])
	res = append(res, val[0])

	return res
}
